/*
 * Copyright (C) 2012  - 2013 The playserver  Project
 * Author:longwuwei
 * All Rights Reserved.
 */

#include "Mediaservice.h"
#include "av_format.h"

Mediaservice::Mediaservice()
{
    HearBeatTick = 60;
    runFlag = 0;
    initFlag = 0;
}

Mediaservice::~Mediaservice()
{
    runFlag = 0;
}

int Mediaservice::Svr_Start(void)
{
    /*
     *
     * init svr config
     */
    if(this->svrConfig.init() != SUCCESS)
    {
        return FAILURE;
    }

    /**
     * init svr log
     */
    this->svrLog.init(this->svrConfig.log_path);
    this->svrLog.set_loglvl(this->svrConfig.loglevel);

    /**
	* init svr file mamange utils class
	*/
	if(this->svrFileManage.init(&this->svrConfig, &this->svrLog) != SUCCESS)
	{
		this->svrLog.log_fmt(LOG_ERR,"svrFileManage init error! \r\n");
		return FAILURE;
	}

    /**
     * init svr info
     */
    this->svrInfo.init(&this->svrConfig,this->get_core_version());

    /*
     * init svr task
     */
    if(this->svrTask.init(&this->svrConfig,&this->svrLog,&this->svrFileManage) != SUCCESS)
    {
        this->svrLog.log_fmt(LOG_ERR,"svrTask init error! \r\n");
        return FAILURE;
    }



    /*
     * init svr client utils
     */
    if(this->svrClient.init(&this->svrConfig, &this->svrTask, &this->svrLog, &this->svrFileManage, DFLT_MIN_CONN_SIZ) != SUCCESS)
    {
        this->svrLog.log_fmt(LOG_ERR,"svrClient init error! \r\n");
        return FAILURE;
    }

    /*
     * init svr net service
     */
    if(this->init(this->svrConfig.listen_ip, this->svrConfig.listen_port, svrConfig.svr_max_connect_count)  != SUCCESS)
    {
        this->svrLog.log_fmt(LOG_ERR,"service init error! \r\n");
        return FAILURE;
    }

    /*
     * svr run flag
     */
    runFlag = 1;

    /*
     * start sverver
     */
    return this->start();
}

int Mediaservice::Svr_Status(void)
{
    return this->runFlag;
}

int Mediaservice::Svr_Stop(void)
{
    if(runFlag == 1)
    {
        printf("NetServer stop...\r\n");
        runFlag = 0;
        return this->stop();
    }
    else
    {
        return 0;
    }
}

/*
* net handle interface
*Svr_TransFile:trans file
*/
NEXT_OPRATION  Mediaservice::Svr_TransFile(user_token * token,buf_chunk * sock_buf)
{
    if(token->filefd >= 0)
    {
        /*
        * set total file trans size
        */
        if(token->trans_size == 0)
        {
            token->trans_size = token->hFile_size;
        }

        /*
        *set trans time out:3600 seconds
        */
        sock_buf->size = 0;
        sock_buf->offset = 0;
        token->have_trans_size = 0;
        token->recv_send_flag = FALSE;

        lseek(token->filefd,token->hFile_offset, SEEK_SET);
        fcntl(token->filefd, F_SETFD, FD_CLOEXEC);
        return DO_TRANSFILE;
    }

    return DO_CLOSE;
}

/*
*inherit virtual function
*/
NEXT_OPRATION Mediaservice::accept_completed(user_token *& token,buf_chunk * sock_buf)
{
    user_token * ptoken = NULL;
    svrClient.alloc(&ptoken);
    ptoken->type = INVALID_TYPE;
    token = ptoken;
    return DO_RECV;
}
/*
*connect_completed
*/
NEXT_OPRATION Mediaservice::connect_completed(user_token * token,buf_chunk * sock_buf)
{
    NEXT_OPRATION nOpt = DO_CLOSE;
    if(token != NULL)
    {
        switch(token->type)
        {
            case SVR_HEARTBEAT:
                {
                    sock_buf->offset = 0;
                    sock_buf->size = svrClient.struct_heartbeat_data(sock_buf->buffer,sock_buf->buf_len,
                    		this->svrClient.get_session_count(),
                    		this->get_connect_pool_use_count());

                    nOpt = DO_SEND;
                    this->svrLog.log_fmt(LOG_INFO,"connect ok,begin send:%s\r\n",sock_buf->buffer);
                }
                break;
            case CDN_AUTH_CLIENT:
				{
					sock_buf->offset = 0;
					sock_buf->size = this->svrClient.struct_auth_request_data(sock_buf->buffer,sock_buf->buf_len,token->key);
					nOpt = DO_SEND;
					this->svrLog.log_fmt(LOG_INFO,"connect ok,begin send:%s\r\n",sock_buf->buffer);
				}
            	break;
            default:
              nOpt = DO_CLOSE;
              break;
        }
    }
    return nOpt;
}
/*
*receive_completed
*/
NEXT_OPRATION  Mediaservice::receive_completed(user_token * token,buf_chunk * sock_buf)
{
    if(token == NULL)
    {
        /*do none, must close*/
        return DO_CLOSE;
    }

    /*token type*/
    switch(token->type)
    {
        case SVR_HEARTBEAT:
        case CDN_AUTH_CLIENT:
			{
				sock_buf->size =  sock_buf->offset;
				sock_buf->offset = 0;
			    return DO_CLOSE;
			}
			break;
        case INVALID_TYPE:
            {
                token->index = 0;
                token->trans_size = 0;
                token->have_trans_size = 0;
                token->tConnTime = time(NULL);
                Client_Type ctype = svrClient.anylsis_client_type(sock_buf->buffer,sock_buf->offset,token);
                /*check is receive completed*/
                if(ctype == ON_RECV_TYPE)
                {
                    /*must < 32 KB*/
                    if(sock_buf->offset >= 32768)
                    {
                        return DO_CLOSE;
                    }
                    /*go on receive*/
                    return DO_RECV;
                }
                else
                {
                    this->svrLog.log_fmt(LOG_ALL,"[%s]Request Data:%s",token->key,sock_buf->buffer);
                    sock_buf->size =  sock_buf->offset;
                    sock_buf->offset = 0;
                }

                /*check request type*/
                if(ctype == PLAYER_CLIENT)
                {
                    return DO_WAIT;
                }
                else if(ctype == DOWNLOAD_CLIENT)
                {
                    return DO_WAIT;
                }
                else if(ctype == HTTP_HTB_CLIENT)
                {
                    /*send response*/
                    sock_buf->size = (ulong)http::struct_response_header(sock_buf->buffer,(HTTP_MIME)token->fmt);
                }
                else if(ctype == HTTP_ADMIN_CLIENT)
                {
                    /*send response*/
                    sock_buf->size = (ulong)http::struct_response_header(sock_buf->buffer,HTML,this->svrInfo.readSvrInfo());
                }
                else if(ctype == HTTP_BROWSE)
                {
                    /*send response*/
                    sock_buf->size = (ulong)http::struct_crossxml_doma_response_header(sock_buf->buffer);
                }
                else
                {
                    /*set type INVALID ,return 400 code*/
                    token->type = INVALID_TYPE;
                    sock_buf->size = (ulong)http::struct_response_header(sock_buf->buffer,(HTTP_MIME)token->fmt,400);
                }
            }
            break;
        default:
        	return DO_CLOSE;
        	break;
    }

    /*default do send*/
    return DO_SEND;
}

/*
*send_completed
*/
NEXT_OPRATION Mediaservice::send_completed(user_token * token,buf_chunk * sock_buf)
{
    if(token == NULL)
    {
          /*do none, must close*/
          return DO_CLOSE;
    }

    switch(token->type)
    {
          case PLAYER_CLIENT:
          case DOWNLOAD_CLIENT:
                  {
                      return this->Svr_TransFile(token,sock_buf);
                  }
                  break;
          case HTTP_BROWSE:
          case HTTP_ADMIN_CLIENT:
          case HTTP_HTB_CLIENT:
                  return DO_CLOSE;
                  break;
          case SVR_HEARTBEAT:
          case CDN_AUTH_CLIENT:
                {
                    token->recv_send_flag = FALSE;
                    sock_buf->offset = 0;
                }
                return DO_RECV;
          default:
                return DO_CLOSE;
                break;
    }
    /*default do recv*/
    return DO_RECV;
}

/**
 *trans_heartbeat,check session is alive
 */
NEXT_OPRATION  Mediaservice::trans_heartbeat(user_token * token)
{
	if(token == NULL)
	{
		  /*do none, must close*/
		  return DO_CLOSE;
	}

	/**
	 * check session status
	 * if not exit ,or auth FAILURE,both to close
	 */
	if(token->type == PLAYER_CLIENT)
	{
		if(AUTH_SUCCESS != this->svrClient.get_client_session_auth_status(token->key))
		{
			//token->have_trans_size
			return DO_CLOSE;
		}
	}

	/*
	 * go on trans file
	 * */
	return DO_TRANSFILE;
}

/*
*trans_completed
*/
NEXT_OPRATION  Mediaservice::trans_completed(user_token * token,buf_chunk * sock_buf)
{
     this->svrLog.log_fmt(LOG_INFO,"trans completed!\r\n");
     return DO_CLOSE;
}

/*
*send_wait_compledted
*/
NEXT_OPRATION Mediaservice::recv_or_send_wait_completed(user_token * token,buf_chunk * sock_buf)
{
    if(token == NULL)
    {
        return DO_CLOSE;
    }

    switch(token->type)
    {
        case PLAYER_CLIENT:
            {
                /*check wait is time out*/
                if(time(NULL) - token->tConnTime > 10)
                {
                     return DO_CLOSE;
                }

                /*
                *get session auth status
                *find client session,and check client session is success,but not HTTP_LIVE_NODE
                */
                AUTH_STATUS status = this->svrClient.get_client_session_auth_status(token->key);
                if(status == AUTH_FAILURE || status == AUTH_DISCONN_WAIT)
                {
                     return DO_CLOSE;
                }
                else if(status == AUTH_WAIT)
                {
                     return DO_WAIT;
                }
                else
                {
                     //add your code here
                }

                /*
                *set first read flag = TRUE
                */
                token->recv_send_flag = TRUE;
                memset(sock_buf->buffer,'\0',sock_buf->buf_len);
                if(token->trans_size == 0)
                {
                     sock_buf->size = (ulong)http::struct_response_header(sock_buf->buffer,(HTTP_MIME)token->fmt,token->hFile_size);
                }
                else
                {
                    if(token->fmt == FLV || token->fmt == FHV)
                    {
                        /*get video header*/
                        ubyte * buf = new ubyte[4096];
                        if(buf == NULL)
                        {
                            this->svrLog.log_fmt(LOG_INFO,"get video header,alloc memory error!");
                            return DO_CLOSE;
                        }
                        int len = av_format::read_flv_header(token->filefd,buf,4096);
                        if(len <= 0)
                        {
                            /*free buf*/
                            delete buf;
                            buf = NULL;

                            this->svrLog.log_fmt(LOG_INFO,"get video header error,code:%d!",len);
                            return DO_CLOSE;
                        }
                        /*struct header*/
                        sock_buf->size = (ulong)http::struct_response_header(sock_buf->buffer,(HTTP_MIME)token->fmt,token->trans_size + len);
                        memcpy(sock_buf->buffer + sock_buf->size,buf,len);
                        sock_buf->size += len;

                        /*free buf*/
                        delete buf;
                        buf = NULL;
                    }
                    else
                    {
                         sock_buf->size = (ulong)http::struct_response_header(sock_buf->buffer,(HTTP_MIME)token->fmt,token->hFile_offset,token->hFile_offset + token->trans_size - 1,token->hFile_size);
                    }
                }

                /*send*/
                this->svrLog.log_fmt(LOG_ALL,"[%s]Response Data:%s",token->key,sock_buf->buffer);
                return DO_SEND;
                /*end*/
            }
            break;
        case DOWNLOAD_CLIENT:
            {
                token->recv_send_flag = TRUE;
                sock_buf->offset = 0;
                memset(sock_buf->buffer,'\0',sock_buf->buf_len);
                if(token->trans_size == 0)
                {
                    sock_buf->size = (ulong)http::struct_response_header(sock_buf->buffer,(HTTP_MIME)token->fmt,token->hFile_size);
                }
                else
                {
                    sock_buf->size = (ulong)http::struct_response_header(sock_buf->buffer,(HTTP_MIME)token->fmt,token->hFile_offset,token->hFile_offset + token->trans_size - 1,token->hFile_size);
                }
                this->svrLog.log_fmt(LOG_ALL,"[%s]Response Data:%s",token->key,sock_buf->buffer);
                return DO_SEND;
            }
            break;
        default:
            return DO_CLOSE;
            break;
    }

    return DO_WAIT;
}

/*
*send_wait_compledted
*/
void Mediaservice::close_completed(user_token * token,buf_chunk * sock_buf,NET_ERROR_CODE error)
{
    /*connect close log*/
    if(error != INVALID_ERROR && error != CLIENT_CLOSED)
    {
        this->svrLog.log_fmt(LOG_INFO,"client close completed,code:%d",error);
    }
    /*handle close opration*/
    if(token != NULL)
    {
        switch(token->type)
        {
            case PLAYER_CLIENT:
                {
                	 this->svrClient.close_client_session(token->key,FALSE,token->have_trans_size);
                }
                break;
            case SVR_HEARTBEAT:
                {
                	/*handle heartbeat data*/
					this->svrTask.handle_heartbeat_data(sock_buf->buffer,sock_buf->size);
					this->svrLog.log_fmt(LOG_INFO,sock_buf->buffer);
                    svrTask.svr_heartbeat_task = TASK_CLOSE;
                }
                break;
            case CDN_AUTH_CLIENT:
				{
					/**
					 * must go to anylsis auth result
					 * and remove client session
					 */
					this->svrClient.anylsis_auth_result(sock_buf->buffer,sock_buf->size,token->key);
				}
				break;
            default:break;
        }

        /*
        *must free token
        */
        svrClient.free(token);
    }
}

/*
*svr log
*/
int  Mediaservice::log_fmt(int nloglvl,const char * lpszFormat , ...)
{
    va_list argList;
    va_start(argList, lpszFormat);

    char m_szMsg[8192];
    memset(m_szMsg,'\0',8192);
    vsprintf(m_szMsg, lpszFormat, argList);
    this->svrLog.log_fmt_arglist(nloglvl,lpszFormat,argList);

    va_end(argList);

    return SUCCESS;
}

/**
 * Mediaservice HearBeat
 */
void  Mediaservice::Svr_HearBeat(void)
{
    /*HearBeat*/
    if(svrTask.svr_heartbeat_task == TASK_CLOSE)
    {
        /*update config*/
        this->svrConfig.update();
        this->svrConfig.set_authsvr_address();

        /*get stream post connect*/
         svrTask.svr_heartbeat_task = TASK_RUN;

         user_token * token = NULL;
         svrClient.alloc(&token);
         token->type = SVR_HEARTBEAT;
         token->local_addr.sin_family = AF_INET;
         token->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
         token->local_addr.sin_port = htons(0);
         token->remote_addr.sin_family = AF_INET;
         token->remote_addr.sin_addr.s_addr = inet_addr(this->svrConfig.auth_ip);
         token->remote_addr.sin_port = htons(this->svrConfig.auth_port);
         post_usr_connect(token,NET_TCP);
    }
}

/**
 * Mediaservice Run task
 */
void  Mediaservice::Svr_Run_task(void)
{

}

/**
 * Mediaservice Handle_Client_Session
 */
void  Mediaservice::Svr_Handle_Client_Session(void)
{
	/*auth*/
	int i = 0;
	for(i = 0;i < 100 ;i++)
	{
		user_token * token = this->svrClient.get_next_session_auth_task();
		if(token == NULL)
		{
			break;
		}
		else
		{
			this->svrLog.log_save_db(LOG_INFO,"[%s][%d] Auth begin,authip: %s:%d",token->key,token->type,this->svrConfig.auth_ip,this->svrConfig.auth_port);
			token->local_addr.sin_family = AF_INET;
			token->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
			token->local_addr.sin_port = htons(0);
			token->remote_addr.sin_family = AF_INET;
			token->remote_addr.sin_addr.s_addr = inet_addr(this->svrConfig.auth_ip);
			token->remote_addr.sin_port = htons(this->svrConfig.auth_port);
			post_usr_connect(token,NET_TCP);
		}
	}
}

/**
 * Mediaservice main worker thread
 */
int Mediaservice::Svr_Run(long useconds)
{
    if(initFlag == 0)
    {
        initFlag = 1;
    }

    /*
     * svr heartbeat
     */
    if(this->HearBeatTick * useconds >= 10000000)
    {
        Svr_HearBeat();

        this->HearBeatTick = 0;
    }

    /*
     * run task
     */
    this->Svr_Run_task();

    /*
     * handle client session
     */
    this->Svr_Handle_Client_Session();

    /*
	 * svrClient handle run
	 * */
	this->svrClient.run(useconds);


	/**
	 * svr file manage run
	 */
	this->svrFileManage.run(useconds);

	/**
	 *refresh  server info
	 */
	this->svrInfo.refresh(useconds,this->get_connect_pool_use_count(),
	        this->get_connect_pool_unuse_count(),
	        this->get_connect_pool_count(),
	        this->get_buf_pool_unuse_count(),
	        this->get_buf_pool_count(),
	        this->svrClient.get_free_count(),
	        this->svrClient.get_count(),
	        this->svrClient.get_session_count(),
	        this->svrClient.get_free_session_count());

    /*
     *time tick
     */
    this->HearBeatTick ++;

    /**
     * return no error
     */
    return 0;
}
